import 'dart:io';

import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';

const httpRequestCanceled = 901;
const httpConnectTimeout = 902;
const httpReceiveTimeout = 903;

class HttpRequest {
  static HttpRequest? _instance;

  static HttpRequest shareInstance() => _instance ??= HttpRequest();

  Dio? _dio;
  LogInterceptor? _logInterceptor;

  static CookieManager? _cookieManager;

  ///设置Host
  Future<HttpRequest> setHost(String host,
      {int connectTimeout = 10, int receiveTimeout = 60}) async {
    var options = BaseOptions(
        baseUrl: host,
        connectTimeout: Duration(seconds: connectTimeout),
        receiveTimeout: Duration(seconds: receiveTimeout),
        extra: {"withCredentials": true});

    _dio = Dio(options);
    if (!kIsWeb) {
      var appDir = await getApplicationDocumentsDirectory();
      if (_cookieManager == null) {
        var cj = PersistCookieJar(storage: FileStorage(appDir.path));
        _cookieManager = CookieManager(cj);
      }
      _dio!.interceptors.add(_cookieManager!);
    }
    return Future.value(this);
  }

  void setProxy({String? proxy}) {
    if (kReleaseMode || _dio == null || kIsWeb) {
      return;
    }
    if (proxy?.isNotEmpty == true) {
      (_dio!.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
        var client = HttpClient();
        // config the http client
        client.findProxy = (uri) {
          //proxy all request to localhost:8888
          return 'PROXY $proxy';
        };
        return client;
      };
    }
  }

  HttpRequest setDebug(bool debug) {
    if (_dio != null) {
      if (debug) {
        //调试状态下开启日志
        _logInterceptor ??=
            LogInterceptor(requestBody: true, responseBody: true);
        if (!_dio!.interceptors.contains(_logInterceptor)) {
          _dio!.interceptors.add(_logInterceptor!);
        }
      } else {
        if (_dio!.interceptors.contains(_logInterceptor)) {
          _dio!.interceptors.remove(_logInterceptor);
        }
      }
    }
    return this;
  }

  Future<HttpResponse<T>> _handleDioError<T>(DioError e) {
    int? code = 900;
    String? msg = '抱歉！请求出错~';
    switch (e.type) {
      case DioExceptionType.cancel:
        msg = '请求取消';
        code = httpRequestCanceled;
        break;
      case DioExceptionType.connectionTimeout:
        msg = '网络连接超时';
        code = httpConnectTimeout;
        break;
      case DioExceptionType.receiveTimeout:
        msg = '请求超时';
        code = httpReceiveTimeout;
        break;
      case DioExceptionType.connectionError:
        msg = '网络连接错误';
        break;
      case DioExceptionType.badResponse:
        code = e.response?.statusCode;
        if (e.response?.data != null) {
          if (e.response!.data is Map && e.response!.data['message'] != null) {
            msg = e.response!.data['message'];
          } else {
            msg = e.response!.statusMessage;
          }
        } else {
          msg = e.response?.statusMessage;
        }
        break;
      default:
        msg = e.message;
        break;
    }

    return Future.value(HttpResponse(
        code: code, message: msg, headers: e.response?.headers.map));
  }

  ///移除null 数据
  void _removeNullValueOfMap(Map<String, dynamic>? data) {
    var removes = [];

    data?.forEach((key, value) {
      if (value != null && value is List) {
        _removeNullValueOfList(value);
        // if (value.isEmpty) {
        //   removes.add(value);
        // }
      } else if (value == null) {
        removes.add(value);
      }
    });
    data?.removeWhere((key, value) {
      return removes.contains(value);
    });
  }

  ///移除null 数据
  void _removeNullValueOfList(List? data) {
    var removes = [];
    data?.forEach((l) {
      if (l is Map) {
        _removeNullValueOfMap(Map<String, dynamic>.from(l));
        if (l.isEmpty) {
          removes.add(l);
        }
      }
    });
    data?.removeWhere((d) {
      return removes.contains(d);
    });
  }

  ///数据请求
  ///method: GET/POST/DELETE/PUT
  ///queryParams: URL 后面拼接传参
  ///data: Body 传参
  Future<HttpResponse> request(String path, HttpMethod method,
      {Map<String, dynamic>? queryParams,
      dynamic data,
      RequestContentType? contentType = RequestContentType.json,
      Map<String, dynamic>? headers,
      CancelToken? cancelToken}) async {
    if (_dio == null) {
      return Future.error('Unknown request');
    }
    try {
      Response res;
      if (queryParams?.isNotEmpty == true) {
        _removeNullValueOfMap(queryParams);
      }
      if (data != null) {
        if (data is Map) {
          _removeNullValueOfMap(Map<String, dynamic>.from(data));
        } else if (data is List) {
          _removeNullValueOfList(data);
        }
      }
      var options = Options(headers: headers);
      if (contentType != null && method != HttpMethod.get) {
        switch (contentType) {
          case RequestContentType.form:
            options.contentType = Headers.formUrlEncodedContentType;
            break;
          case RequestContentType.json:
            options.contentType = Headers.jsonContentType;
            break;
          case RequestContentType.text:
            options.contentType = 'application/text';
            break;
          case RequestContentType.multiForm:
            options.contentType = 'multipart/form-data';
            break;
        }
      }
      switch (method) {
        case HttpMethod.get:
          res = await _dio!.get<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              cancelToken: cancelToken,
              options: options);
          break;
        case HttpMethod.delete:
          res = await _dio!.delete<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        case HttpMethod.post:
          res = await _dio!.post<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        case HttpMethod.put:
          res = await _dio!.put<Map<String, dynamic>>(path,
              queryParameters: queryParams,
              data: data,
              cancelToken: cancelToken,
              options: options);
          break;
        default:
          return Future.error('Method $method not support');
      }
      if (res.data?['code'] != null) {
        return Future.value(HttpResponse(
            code: res.data['code'],
            headers: res.headers.map,
            message: res.data['message'],
            data: res.data['data']));
      } else {
        return Future.value(
            HttpResponse(code: 200, headers: res.headers.map, data: res.data));
      }
    } on DioException catch (e) {
      return _handleDioError(e);
    }
  }

  Future<Response<String>> getApkResponse(String apkUrl) {
    if (_dio == null) {
      return Future.error('Unknown request');
    }
    return _dio!.get<String>(apkUrl);
  }
}

class HttpResponse<T> {
  HttpResponse({this.code, this.message, this.headers, this.data});

  factory HttpResponse.empty() {
    return HttpResponse(code: -1, message: '空数据');
  }

  ///response code
  int? code;

  ///error message
  String? message;

  ///response headers
  Map<String, List<String>>? headers;

  ///response data
  T? data;

  Map<String, dynamic> toMap() => {
        'code': code,
        'message': message,
        'data': data,
      };
}

enum HttpMethod { get, put, post, delete }

enum RequestContentType {
  json,
  form,
  text,
  multiForm,
}
